package com.xuecheng.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.xuecheng.api.order.model.pay.PayCodeUrlResult;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.enums.order.OrderDealStatusEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.order.agent.LearningApiAgent;
import com.xuecheng.order.common.constant.OrderErrorCode;
import com.xuecheng.order.entity.Orders;
import com.xuecheng.order.entity.Pay;
import com.xuecheng.order.mapper.PayMapper;
import com.xuecheng.order.service.OrdersService;
import com.xuecheng.order.service.PayService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * <p>
 * 订单支付信息 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class PayServiceImpl extends ServiceImpl<PayMapper, Pay> implements PayService {

    @Autowired
    private WxPayService wxPayService;

    @Autowired
    private OrdersService ordersService;

    @Autowired
    private LearningApiAgent learningApiAgent;

    @Override
    @GlobalTransactional
    public void notifyPayment(String xmlResultString) {
        if (StringUtil.isBlank(xmlResultString)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }

        WxPayOrderNotifyResult notifyResult = null;
        try {
            // 解析wx通知内容
            notifyResult = wxPayService.parseOrderNotifyResult(xmlResultString);
        } catch (WxPayException e) {
            log.error(OrderErrorCode.E_160018.getDesc() + ": errMsg => {}" + e.getMessage());
            ExceptionCast.cast(OrderErrorCode.E_160018);
        }

        // 支付通过结果的 return_code & result_code 必须为SUCCESS
        String returnCode = notifyResult.getReturnCode();
        String resultCode = notifyResult.getResultCode();
        // 获取商户订单号
        String outTradeNo = notifyResult.getOutTradeNo();
        // 支付成功
        if (PayCodeUrlResult.WX_PAY_SUCCESS_FLAG.equalsIgnoreCase(returnCode) && PayCodeUrlResult.WX_PAY_SUCCESS_FLAG.equalsIgnoreCase(resultCode)) {
            // 微信支付平台会重复发送消息，这里要保证消息幂等性
            LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper<>();
            // 查询订单号及状态为初始态的订单信息
            queryWrapper.eq(Orders::getOrderNo, outTradeNo);
            queryWrapper.eq(Orders::getStatus, Integer.valueOf(OrderDealStatusEnum.ORDER_DEAL_INIT_STATUS.getCode()));
            Orders order = ordersService.getOne(queryWrapper);
            // 若订单为空则抛出异常
            if (ObjectUtils.isEmpty(order)) {
                log.error("该订单信息不是初始状态，订单编号 => {}", outTradeNo);
                ExceptionCast.castWithExceptionMsg(OrderErrorCode.E_160014, OrderErrorCode.E_160014.getDesc());
            }

            // 判断第三方订单金额是否和商户金额一致
            // wx订单金额（分）
            Integer totalFee = notifyResult.getTotalFee();
            String toYuan = BaseWxPayResult.fenToYuan(totalFee);
            Float totalFeeFloat = new Float(toYuan);

            // 商户订单金额（元）
            Float price = order.getPrice();
            // 判断金额是否一致
            if (!(totalFee.equals(price))) {
                ExceptionCast.castWithExceptionMsg(OrderErrorCode.E_160019, OrderErrorCode.E_160019.getDesc());
            }

            // 修改订单状态
            LambdaUpdateWrapper<Orders> ordersUpdateWrapper = new LambdaUpdateWrapper<>();
            ordersUpdateWrapper.set(Orders::getStatus, Integer.valueOf(OrderDealStatusEnum.ORDER_DEAL_PAID_STATUS.getCode()));
            ordersUpdateWrapper.set(Orders::getChangeDate, LocalDateTime.now());
            ordersUpdateWrapper.eq(Orders::getOrderNo, outTradeNo);
            boolean orderUpdateResult = ordersService.update(ordersUpdateWrapper);
            // 若业务数据操作失败，需要让wx平台继续通知，继续通知的方式是业务层对外抛出异常
            if (!orderUpdateResult) {
                ExceptionCast.cast(OrderErrorCode.E_160015);
            }

            // 订单支付数据
            LambdaQueryWrapper<Pay> payQueryWrapper = new LambdaQueryWrapper<>();
            // 订单支付信息状态应该为未支付
            payQueryWrapper.eq(Pay::getOrderId, order.getId());
            payQueryWrapper.eq(Pay::getStatus, PayCodeUrlResult.NOT_PAY);
            Pay pay = this.getOne(payQueryWrapper);
            if (ObjectUtils.isEmpty(pay)) {
                log.error("订单支付信息不是未支付状态，订单编号 => {}", outTradeNo);
                ExceptionCast.castWithExceptionMsg(OrderErrorCode.E_160020, OrderErrorCode.E_160020.getDesc());
            }

            // 设置状态为已支付
            pay.setStatus(PayCodeUrlResult.PAIED);
            // 设置wx的支付编号
            pay.setPayNumber(notifyResult.getTransactionId());

            // 记录wx支付平台的用户支付时间
            String timeEnd = notifyResult.getTimeEnd();
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
            LocalDateTime payTime = LocalDateTime.parse(timeEnd, dateTimeFormatter);
            pay.setPayDate(payTime);

            // 获取应结订单金额
            Integer settlementTotalFee = notifyResult.getSettlementTotalFee();
            if (!ObjectUtils.isEmpty(settlementTotalFee)) {
                String settlementTotalFeeString = BaseWxPayResult.fenToYuan(settlementTotalFee);
                // 设置实收金额
                pay.setReceiptAmount(new BigDecimal(settlementTotalFeeString));
            }

            // 获取现金支付金额
            Integer cashFee = notifyResult.getCashFee();
            if (!ObjectUtils.isEmpty(cashFee)) {
                String cashFeeString = BaseWxPayResult.fenToYuan(cashFee);
                // 设置付款金额
                pay.setBuyerPayAmount(new BigDecimal(cashFeeString));
            }
            // 设置支付响应
            pay.setPayResponse(xmlResultString);

            // 更新订单支付信息
            boolean payUpdateResult = this.updateById(pay);
            ExceptionCast.cast(!payUpdateResult, OrderErrorCode.E_160017);

            // 用户支付成功后创建一个默认的学习记录
            learningApiAgent.createCourseRecord4s(order.getUserName(), order.getCoursePubId());
        } else {
            // 支付失败
            log.error("支付通知内容失败，订单编号 => {}", outTradeNo);
            ExceptionCast.cast(OrderErrorCode.E_160010);
        }
    }
}
